﻿using System;
using System.Collections.Generic;
using Microsoft.Practices.SharePoint.Common.Logging;
using Microsoft.SharePoint.Administration;

namespace SharePoint.Contrib.Diagnostics
{
    /// <summary>
    /// Internal implementation of IDiagnosticsService
    /// </summary>
    internal class SPCDiagnosticsService : SPDiagnosticsServiceBase, IDiagnosticsService
    {
        ILogger LoggingProvider;

        public SPCDiagnosticsService()
            :base(Constants.Areas.SPC, SPFarm.Local)
        {
            this.LoggingProvider = SPG.Logger;
        }

        private SPDiagnosticsCategory DiagnosticsCategory
        {
            get
            {
                return Areas[Constants.Areas.SPC].Categories[Constants.Categories.Diagnostics];
            }
        }

        private void HandleException(SPDiagnosticsCategory category, Exception exception)
        {
            try
            {
                 WriteEvent(
                    0,
                    category,
                    EventSeverity.Error,
                    exception.ToString());
            }
            catch { }
        }

        public void HandleException(string area, string category, Exception exception)
        {
            try
            {

                LoggingProvider.LogToOperations(
                    exception,
                    0,
                    EventSeverity.Error,
                    SPG.AreaAndCategory(area, category));
            }
            catch(Exception e)
            {               
                Trace("SPCDiagnosticsService: An error occured while writing an exception");
                HandleException(exception);
                HandleException(e);
            }
        }

        /// <summary>
        /// Ensures that the exception is handled even if no areas/categories have been added using the always available category 'diagnostics'
        /// </summary>
        /// <param name="exception">the exception</param>
        public void HandleException(Exception exception)
        {
            HandleException(DiagnosticsCategory, exception);
        }

        /// <summary>
        /// Ensures that the message is traced even if no areas/categories have been added using the always available category 'diagnostics'
        /// </summary>
        /// <param name="message">the message</param>
        /// <param name="args">optional args for string.format</param>
        public void Trace(string message, params object[] args)
        {
            Trace(DiagnosticsCategory, message, args);
        }

        public void Trace(string area, string category, int messageId, string message, params object[] args)
        {
            try
            {
                LoggingProvider.TraceToDeveloper(
                    string.Format(message, args),
                    messageId,
                    TraceSeverity.Medium,
                    SPG.AreaAndCategory(area, category));
            }
            catch(Exception e)
            {
                Trace("SPCDiagnosticsService: An error occured while writing a trace message.");
                HandleException(e);
            }
        }

        public void Trace(string area, string category, string message, params object[] args)
        {
            Trace(area, category, 0, message, args);
        }

        private void Trace(SPDiagnosticsCategory category, string message, params object[] args)
        {
            try
            {
                WriteTrace(
                    0,
                    category,
                    category.TraceSeverity,
                    message,
                    args);
            }
            catch { }
        }

        protected override IEnumerable<SPDiagnosticsArea> ProvideAreas()
        {
            // this is done just so we can use area+category while adding new areas and categories via the AreasAndCategoriesService
            // since it is not registred while doing that
            return new List<SPDiagnosticsArea>
            {
                new SPDiagnosticsArea(Constants.Areas.SPC, new List<SPDiagnosticsCategory>
                {
                    new SPDiagnosticsCategory(Constants.Categories.Diagnostics, TraceSeverity.Medium, EventSeverity.Error)
                })
            };
        }
    }
}
